package com.personal.common.util;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

@SuppressWarnings({"unused"})
public class Des {
	// initial permutation IP
	private static byte[] IP_Table = { // 64
	58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32,
			24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63,
			55, 47, 39, 31, 23, 15, 7 };

	// final permutation IP^-1
	private static byte[] IPR_Table = { // 64
	40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21,
			61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1,
			41, 9, 49, 17, 57, 25 };

	// expansion operation matrix
	private static byte[] E_Table = { // 48
	32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18, 19, 20, 21, 20, 21,
			22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1 };

	// 32-bit permutation function P used on the output of the S-boxes
	private static byte[] P_Table = { // 32
	16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4,
			25 };

	// permuted choice table (key)
	private static byte[] PC1_Table = { // 56
	57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55,
			47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 };

	// permuted choice key (table)
	private static byte[] PC2_Table = { // 48
	14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30,
			40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 };

	// number left rotations of pc1
	private static byte[] LOOP_Table = { // 16
	1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };

	// The (in)famous S-boxes
	private static byte[][][] S_Box = { // 8,4,16
	// S1
			{ { 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7 },
					{ 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8 },
					{ 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0 },
					{ 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 } },
			// S2
			{ { 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10 },
					{ 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5 },
					{ 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15 },
					{ 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 } },
			// S3
			{ { 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8 },
					{ 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1 },
					{ 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7 },
					{ 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 } },
			// S4
			{ { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15 },
					{ 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9 },
					{ 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4 },
					{ 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 } },
			// S5
			{ { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9 },
					{ 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6 },
					{ 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14 },
					{ 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 } },
			// S6
			{ { 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11 },
					{ 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8 },
					{ 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6 },
					{ 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 } },
			// S7
			{ { 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1 },
					{ 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6 },
					{ 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2 },
					{ 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 } },
			// S8
			{ { 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7 },
					{ 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2 },
					{ 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8 },
					{ 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 } } };

	private static byte[] desKey; // 16
	private static String[][] subKey = new String[2][16]; // 2,16

	private static boolean is3DES = false;

	enum Type {
		ENCRYPT, DECRYPT
	}

	private static String[][] setKey(byte[] key, int keyLen) {

		desKey = memset(0, 16);
		desKey = memcpy(key, 0, keyLen > 16 ? 16 : keyLen);
		byte[] tempL = new byte[8];

		tempL = memcpy(desKey, 0, 8);
		subKey[0] = setSubKey(tempL);

		is3DES = keyLen > 8 ? true : false;
		if (keyLen <= 8) {
			return subKey;
		} else {
			byte[] tempR = new byte[8];
			subKey[1] = setSubKey(tempR);
			return subKey;
		}
	}

	private static byte[] SDES(byte[] in, String[] subKey, Type type) {
		/*int[] k = new int[2];
		k = bytes2int(in, 8);
		String kStr = Integer.toBinaryString(k[1]) + Integer.toBinaryString(k[0]);*/
		String kStr = bytes2bstring(in);
		kStr = transform(kStr, IP_Table, 64);

		String l = kStr.substring(0, 32);
		String r = kStr.substring(32, 64);
		String temp = new String();
		if (type == Type.ENCRYPT) {
			for (int i = 0; i < 16; i++) {
				temp = r;
				r = f_func(r, subKey[i]);
				r = xor(r, l, 32);
				l = temp;
			}
		} else {
			for (int i = 15; i >= 0; i--) {
				temp = l;
				l = f_func(l, subKey[i]);
				l = xor(l, r, 32);
				r = temp;
			}
		}
		kStr = transform(l + r, IPR_Table, 64);
		return binaryString2bytes(kStr);
	}

	private static String f_func(String in, String ki) {
		String mr = transform(in, E_Table, 48);
		mr = xor(mr, ki, 48);
		String p = s_func(mr);
		return transform(p, P_Table, 32);
	}

	private static String s_func(String binaryStr) {
		StringBuffer result = new StringBuffer();
		
		for (int i = 0, offset = 0; i < 8; i++, offset += 6) {
			int j1 = Integer.valueOf(String.valueOf(binaryStr.charAt(offset)));
			j1 = j1 << 1;
			int j2 = Integer.valueOf(String.valueOf(binaryStr.charAt(offset + 5)));
			int j = j1 + j2;
			
			int k1 = Integer.valueOf(String.valueOf(binaryStr.charAt(offset + 1)));
			k1 = k1 << 3;
			int k2 = Integer.valueOf(String.valueOf(binaryStr.charAt(offset + 2)));
			k2 = k2 << 2;
			int k3 = Integer.valueOf(String.valueOf(binaryStr.charAt(offset + 3)));
			k3 = k3 << 1;
			int k4 = Integer.valueOf(String.valueOf(binaryStr.charAt(offset + 4)));
			int k = k1 + k2 + k3 + k4;
			
			byte[] by = new byte[1];
			by[0] = S_Box[i][j][k];
			
			String temp = bytes2bstring(by);
			result.append(temp.substring(0, 4));
		}
		
		return result.toString();
	}

	private static String alignBinaryString(String bStr) {
		String temp = new String("");
		if (bStr.length() < 32) {
			for (int i = 0; i < 32 - bStr.length(); i++) {
				temp = temp + "0";
			}
			temp = temp + bStr;
		}
		return temp;
	}

	private static String[] setSubKey(byte[] key) {
		/*int[] k = new int[2];
		k = bytes2int(key, 8);
		String kStr = Integer.toBinaryString(k[0]) + Integer.toBinaryString(k[1]);*/
		String kStr = bytes2bstring(key);
		String k56 = transform(kStr, PC1_Table, 56);

		String[] subKey = new String[16];
		for (int i = 0; i < 16; ++i) {
			k56 = rotateL(k56.substring(0, 28), 28, LOOP_Table[i]) + rotateL(k56.substring(28, 56), 28, LOOP_Table[i]);
			subKey[i] = transform(k56, PC2_Table, 48);
		}
		return subKey;
	}

	private static String bytes2bstring(byte[] b) {
		char[] temp = new char[8];
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < b.length; i++) {
			temp[0] = (b[i] & 0x1) == 0 ? '0' : '1';
			temp[1] = (b[i] >> 1 & 0x1) == 0 ? '0' : '1';
			temp[2] = (b[i] >> 2 & 0x1) == 0 ? '0' : '1';
			temp[3] = (b[i] >> 3 & 0x1) == 0 ? '0' : '1';
			temp[4] = (b[i] >> 4 & 0x1) == 0 ? '0' : '1';
			temp[5] = (b[i] >> 5 & 0x1) == 0 ? '0' : '1';
			temp[6] = (b[i] >> 6 & 0x1) == 0 ? '0' : '1';
			temp[7] = (b[i] >> 7 & 0x1) == 0 ? '0' : '1';

			sb.append(temp);
		}

		return sb.toString();
	}

	private static String transform(String in, byte[] table, int len) {
		char[] temp = new char[len];
		for (int i = 0; i < len; i++) {
			temp[i] = in.charAt(table[i] - 1);
		}
		return new String(temp);
	}

	private static String rotateL(String src, int len, int count) {
		return src.substring(count, len) + src.substring(0, count);
	}

	private byte[] xor(byte[] inbuff, byte[] outbuff, int count) {
		for (int i = 0; i < count; i++) {
			outbuff[i] ^= inbuff[i];
		}
		return outbuff;
	}

	private static String xor(String inbuff, String outbuff, int count) {
		char[] a = inbuff.toCharArray();
		char[] b = outbuff.toCharArray();
		char[] temp = new char[count];
		for (int i = 0; i < count; i++) {
			if (a[i] + b[i] == 97) {
				temp[i] = '1';
			} else {
				temp[i] = '0';
			}
		}
		return new String(temp);
	}

	private static byte[] memset(int ch, int count) {
		byte[] dest = new byte[count];
		Arrays.fill(dest, (byte) ch);
		return dest;
	}

	private static byte[] memcpy(byte[] src, int offset, int count) {
		byte[] dest = new byte[count];
		for (int i = 0; i < count; i++) {
			dest[i] = src[offset + i];
		}
		return dest;
	}

	public static byte[] long2bytes(long number) {
		long temp = number;
		byte[] b = new byte[8];
		for (int i = 0; i < b.length; i++) {
			b[i] = new Long(temp & 0xff).byteValue();// 将最低位保存在最低位
			temp = temp >> 8; // 向右移8位
		}
		return b;
	}

	private byte[] int2bytes(int res) {
		byte[] tar = new byte[4];

		tar[0] = (byte) (res & 0xff);
		tar[1] = (byte) ((res >> 8) & 0xff);
		tar[2] = (byte) ((res >> 16) & 0xff);
		tar[3] = (byte) ((res >> 24) & 0xff);

		return tar;
	}

	public static int[] bytes2int(byte[] res, int count) {
		int[] temp = new int[count / 4];
		int n = 0;
		for (int i = 0; i < count; i += 4) {
			// 一个byte数据左移24位变成0x??000000，再右移8位变成0x00??0000
			int targets = (res[i + 0] & 0xff) | ((res[i + 1] << 8) & 0xff00) | ((res[i + 2] << 24) >>> 8)
					| (res[i + 3] << 24);
			temp[n++] = targets;
		}
		return temp;
	}

	private static byte[] arraycat(byte[] buf1, byte[] buf2) {
		byte[] bufret = null;
		int len1 = 0;
		int len2 = 0;
		if (buf1 != null)
			len1 = buf1.length;
		if (buf2 != null)
			len2 = buf2.length;
		if (len1 + len2 > 0)
			bufret = new byte[len1 + len2];
		if (len1 > 0)
			System.arraycopy(buf1, 0, bufret, 0, len1);
		if (len2 > 0)
			System.arraycopy(buf2, 0, bufret, len1, len2);
		return bufret;
	}

	private static byte[] subByteArray(byte[] src, int beginIndex, int length) {
		byte[] temp = new byte[length];
		for (int i = 0; i < length; i++) {
			temp[i] = src[beginIndex + i];
		}
		return temp;
	}

	public static byte[] DesGo(byte[] in, int dataLen, byte[] key, int keyLen, Type type) {
		if (dataLen % 8 != 0) {
			dataLen++;
		}

		String[][] subKey = setKey(key, keyLen);
		byte[] bt = SDES(subByteArray(in, 0, 8), subKey[0], type);
		for (int i = 8; i < dataLen; i += 8) {
			bt = arraycat(bt, SDES(subByteArray(in, i, 8), subKey[0], type));
		}
		return bt;
	}

	public static byte[] binaryString2bytes(String binaryString) {
		int size = binaryString.length() / 8;
		byte[] b = new byte[size];
		for (int i = 0; i < size; i++) {
			String s = binaryString.substring(i * 8, (i + 1) * 8);
			StringBuffer sb = new StringBuffer(s);
			sb.reverse();
			s = sb.toString();
			b[i] = Integer.valueOf(s, 2).byteValue();
		}
		
		return b;
	}

	public static void main(String[] args) throws UnsupportedEncodingException {
//		String licenseId = "416C8ED66B8DF6BD830CE2F0A54DDBB1";
//		byte[] ab = DesGo(licenseId.getBytes("UTF-8"), licenseId.length(), "dP1<g3m&".getBytes("UTF-8"), 8,
//				Type.ENCRYPT);

		/*String aString = "1000001010101100001011000010001000100010010000100100001010001100";
		byte[] abc = binaryString2bytes("1111000010001000");
		Long a = Long.parseLong("1111000010001000", 2);
		byte[] abcd = long2bytes(a);*/
		
		byte[] a = {127, 64};
		System.out.println(bytes2bstring(a));
	}

}
